#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <UIKit/UIApplication.h>
#import <LockInfo/Plugin.h>
#import <EventKit/EKEvent.h>

#import "framework/FWCommon.h"
#import "framework/FWApplication.h"
#import "framework/FWPreferences.h"

#import "PICommon.h"
#import "PIIntegration.h"
#import "PITaskPlugin.h"
#import "PITaskStatus.h"

#import "PITaskNative.h"
#import "PITask.h"
#import "PIAlarm.h"
#import "PIRecurrance.h"
#import "PIAttendee.h"


static NSInteger const kMaxSearchItems = 50;


@implementation PITaskNative

+ (PITask*)basicTask:(id)task bundle:(NSBundle*)bundle timeZone:(NSTimeZone*)timeZone
{
    NSDateComponents* startComponents = [task startDateComponents];
    NSDate* startDate = [startComponents date];
    NSDate* dueDate = [task dueDate];
    
    if (!startDate)
        startDate = [NSDate nullDate];
    if (!dueDate)
        dueDate = [NSDate nullDate];
    
    if ([startDate isEqualToDate:dueDate])
        startDate = [NSDate nullDate];
    
    NSTimeZone* tz = [task timeZone];
    if (!tz)
        tz = timeZone;

    PITask* basic = [PITask nativeTaskWithKey:[task UUID]
                                      version:[[task lastModifiedDate] timeIntervalSinceReferenceDate]
                                        title:[task title] ? [task title] : localize(bundle, @"Task/New Task")
                                         icon:nil
                                    startDate:startDate
                            startDateTimeZone:tz
                                 startDateRaw:[startDate timeIntervalSinceReferenceDate]
                                      dueDate:dueDate
                              dueDateTimeZone:tz
                                   dueDateRaw:[dueDate timeIntervalSinceReferenceDate]
                                       allDay:NO
                                     calendar:[[task calendar] title]
                                 calendarIcon:nil
                                calendarColor:[UIColor colorWithCGColor:((UIColor*)[task calendar]).CGColor]
                                    parentKey:nil];
    
    int priority = (int)[task priority];
    if (priority >= 9)
        basic.importance = PIImportanceLow;
    else if (priority >= 5)
        basic.importance = PIImportanceNormal;
    else if (priority >= 1)
        basic.importance = PIImportanceHigh;
    else
        basic.importance = PIImportanceNone;
    
    basic.repeating = [[task recurrenceRules] count] != 0;
    basic.contextClassification = ContextClassificationNoContext;
    
    return basic;
}


// ******************************************************************************************
#pragma mark -
#pragma mark Fetch a list of basic events

+ (NSMutableArray*)basicTaskList:(FWApplication*)app database:(FWDatabase*)database prefs:(FWPreferences*)prefs key:(NSString*)key search:(NSString*)search
{
    if (![prefs boolValue:kPITaskOptionsNativeReminders withDefault:kPITaskOptionsNativeRemindersDefault])
        return [NSMutableArray array];
    
    NSBundle* bundle = [NSBundle bundleForClass:[self class]];
    //NSDate* now = [NSDate date];
    
    EKEventStore* eventStore = ((PITaskPlugin*)app.fwPlugin).eventStore;
    // Can we read iOS 5 calendars?
    if (![eventStore respondsToSelector:@selector(readWriteCalendarsForEntityType:)])
        return [NSMutableArray array];
    
    NSArray* hiddenCals = [prefs arrayValue:kPIOptionsHiddenNativeCalendars withDefault:[NSArray array]];
    NSMutableArray* calendars = [NSMutableArray arrayWithCapacity:eventStore.calendars.count];
    DebugLog(@"LI:PITask: Filtering calendar list for events; hidden = %@", hiddenCals);
    for (EKCalendar* calendar in [(id)eventStore readWriteCalendarsForEntityType:1]) {
        if ([hiddenCals indexOfObject:FWKeyForCalendar(calendar)] == NSNotFound) {
            DebugLog(@"LI:PIEvent: Calendar not in hidden list; %@", calendar);
            [calendars addObject:calendar];
        }
#ifdef DEBUG
        else
            DebugLog(@"LI:PITask: Calendar in hidden list - skipped; %@", calendar);
#endif
    }
    
    if (calendars.count == 0) {
        DebugLog(@"LI:PITask: No calendars left to check for native tasks");
        return [NSMutableArray array];
    }
    
    // Restrict the number of items returned to the requested number		
    int queryLimit = search ? kMaxSearchItems : [prefs intValue:kPIOptionsMaxTasks withDefault:kPIOptionsMaxTasksDefault];

    NSMutableArray* results = [NSMutableArray arrayWithCapacity:queryLimit];
    NSTimeZone* timeZone = [NSTimeZone systemTimeZone];

    if (search) {
        NSPredicate* predicate = [eventStore predicateForRemindersWithTitle:search
                                                                  listTitle:nil
                                               limitToCompletedOrIncomplete:YES
                                                                  completed:NO
                                                                   dueAfter:nil
                                                                  dueBefore:nil
                                                                 searchTerm:nil
                                                                  sortOrder:0];
        
        // Fetch all events that match the predicate.
        NSArray* tasks = [eventStore remindersMatchingPredicate:predicate];
        
        DebugLog(@"LI:PITask: Found %d native tasks", tasks.count);
        NSUInteger items = 0;
        for (id task in tasks) {
            [results addObject:[PITaskNative basicTask:task bundle:bundle timeZone:timeZone]];
            if (++items >= queryLimit)
                break;
        }
    }
    else {
        // If not hiding undated items the get a list of items without dates
        if (![prefs boolValue:kPIOptionsHideUndated withDefault:kPIOptionsHideUndatedDefault]) {
            XLog(@"LI:PITask: Fetching native tasks without dates; calendars = %@", calendars);
            
            NSPredicate *predicate = [eventStore predicateForIncompleteRemindersDueBeforeOrOnDueDate:nil
                                                                                           calendars:calendars];
            NSArray* tasks = [eventStore remindersMatchingPredicate:predicate];
            
            DebugLog(@"LI:PITask: Found %d native tasks", tasks.count);
            NSUInteger items = 0;
            for (id task in tasks) {
                if (![task dueDate]) {
                    [results addObject:[PITaskNative basicTask:task bundle:bundle timeZone:timeZone]];
                    if (++items >= queryLimit)
                        break;
                }
            }
        }
        
        
        // Get the date maxDays in the future
        NSTimeInterval maxDays = (NSTimeInterval)[prefs intValue:kPIOptionsDayLimit withDefault:kPIOptionsDayLimitDefault];
        NSTimeInterval seconds = maxDays * SECONDS_PER_DAY;
        
        NSDate *midnight = FWUTCForLocalDate(FWLocalDateForLocalMidnight());
        XLog(@"LI:PITask: Local midnight utc date = %@", midnight);
        
        // Get future midnight date by adding the number of days we will show (as seconds). Again this will be in UTC
        NSDate* future = [midnight addTimeInterval:seconds];
        //DebugLog(@"LI:PITask: Cutoff date = %@", future);
        
        
        XLog(@"LI:PITask: Fetching native tasks with dates; due = %@; calendars = %@", future, calendars);
        NSPredicate *predicate = [eventStore predicateForIncompleteRemindersDueBeforeOrOnDueDate:future
                                                                                       calendars:calendars];
        
        // Fetch all events that match the predicate.
        NSArray* tasks = [eventStore remindersMatchingPredicate:predicate];
        
        DebugLog(@"LI:PITask: Found %d native tasks", tasks.count);
        NSUInteger items = 0;
        for (id task in tasks) {
            [results addObject:[PITaskNative basicTask:task bundle:bundle timeZone:timeZone]];
            if (++items >= queryLimit)
                break;
        }
    }
    
    return results;
}


+ (PITask*)augmentedBasicTask:(PITask*)source application:(FWApplication*)application
{
    PITask* augment = [[source copy] autorelease];
    augment.augmented = YES;

    EKEventStore* eventStore = ((PITaskPlugin*)application.fwPlugin).eventStore;

    id task = [eventStore reminderWithUUID:augment.key];
    
    if ([[task alarms] count] > 0) {
        NSMutableArray* alarms = [NSMutableArray arrayWithCapacity:[[task alarms] count]];
        for (EKAlarm* alarm in [task alarms]) {
            if (id locn = [alarm structuredLocation]) {
                int proximity = (int)[alarm proximity];
                PIAlarm* newAlarm = [PIAlarm alarmWithKey:@""
                                                     type:proximity == 1 ? kPIEventAlarmTypeLocationArrive : kPIEventAlarmTypeLocationLeave
                                                 location:[[[locn title]
                                                            componentsSeparatedByCharactersInSet:
                                                            [NSCharacterSet newlineCharacterSet]]
                                                           componentsJoinedByString:@" "]
                                              coordinates:[locn geoLocation]];
                [alarms addObject:newAlarm];
            }
            else {
                PIAlarm* newAlarm = alarm.absoluteDate ?
                  [PIAlarm alarmWithKey:@"" date:alarm.absoluteDate type:kPIEventAlarmTypeNative] :
                [PIAlarm alarmWithKey:@"" relativeDate:alarm.relativeOffset baseDate:[task dueDate] type:kPIEventAlarmTypeNative];
                [alarms addObject:newAlarm];
            }
        }
        augment.alarms = alarms;
    }

    if (augment.isRepeating)
        augment.recurrance = [PIRecurrance recurranceWithRule:[[task recurrenceRules] objectAtIndex:0]];

    if ([[task notes] length] > 0)
        augment.notes = [task notes];

    augment.completionDate = [task completionDate];
    source.completionDate = augment.completionDate;
        
    return augment;
}


+ (BOOL)createTask:(NSString *)title importance:(PIImportanceType)importance application:(FWApplication *)app
{
    EKEventStore* eventStore = ((PITaskPlugin*)app.fwPlugin).eventStore;
    
    // Tasks not supported?
    if (![eventStore respondsToSelector:@selector(reminderWithUUID:)])
        return NO;

    id task = [objc_getClass("EKReminder") reminderWithEventStore:eventStore];
    [task setTitle:title];
    
    EKCalendar* defCal = [eventStore defaultCalendarForNewTasks];
    [task setCalendar:(id)defCal];

    NSInteger taskImportance = 0;
    switch (importance) {
        case PIImportanceLow:
            taskImportance = 9;
            break;
            
        case PIImportanceNormal:
            taskImportance = 5;
            break;

        case PIImportanceHigh:
            taskImportance = 1;
            break;

        case PIImportanceTop:
            taskImportance = 1;
            break;

        case PIImportanceNone:
            taskImportance = 0;
            break;
    }
    [task setPriority:(id)taskImportance];
    
    NSError *error;
    BOOL update = [eventStore saveReminder:task error:&error] != 0;
    if (update)
        NSLog(@"LI:PITask: Native task create success; task = %@", task);
    else
        NSLog(@"LI:PITask: Native task create failed; task = %@; error = %@", task, error);
    
    return update;
}

+ (BOOL)completeTask:(PITask*)task application:(FWApplication*)app
{
    EKEventStore* eventStore = ((PITaskPlugin*)app.fwPlugin).eventStore;
    
    // Tasks not supported?
    if (![eventStore respondsToSelector:@selector(reminderWithUUID:)])
        return NO;
    
    if (id native = [eventStore reminderWithUUID:task.key]) {
        [native setCompleted:YES];

        if (![native startDateComponents]) {
            NSDateComponents* comps = [[NSCalendar currentCalendar] components:(NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit | NSCalendarCalendarUnit | NSTimeZoneCalendarUnit | NSEraCalendarUnit) fromDate:[NSDate date]];
            [native setStartDateComponents:comps];
        }
        
        [native setCompletionDate:[NSDate date]];
        
        NSError *error;
        BOOL update = [eventStore saveReminder:native error:&error] != 0;
        if (update)
            NSLog(@"LI:PITask: Native task complete success; task = %@", task);
        else
            NSLog(@"LI:PITask: Native task complete failed; task = %@; error = %@", task, error);
        
        return update;
    }
    
    return NO;
}


+ (NSURL*)urlForShowTask:(PITask*)task application:(FWApplication*)app
{
    PITaskPlugin* plugin = (PITaskPlugin*)app.fwPlugin;
    EKEventStore* eventStore = plugin.eventStore;
    if (id native = [eventStore reminderWithUUID:task.key]) {
        if ([native respondsToSelector:@selector(externalURI)]) {
            NSURL* url = [native externalURI];
            return url;
        }
    }
    
    return nil;
}


+ (BOOL)supportsNativeTasks:(FWApplication*)app
{
    PITaskPlugin* plugin = (PITaskPlugin*)app.fwPlugin;
    EKEventStore* eventStore = plugin.eventStore;

    return [eventStore respondsToSelector:@selector(reminderWithUUID:)];
}

@end